package com.eternal.base.data;

import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;

import androidx.paging.DataSource;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.BitmapFactory;

import androidx.core.app.NotificationCompat;

import android.util.SparseArray;

import com.alibaba.android.arouter.core.LogisticsCenter;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.launcher.ARouter;
import com.eternal.base.NotificationDialog;
import com.eternal.base.R;
import com.eternal.base.concat.BluetoothEvent;
import com.eternal.base.concat.LogExtra;
import com.eternal.base.concat.NotificationName;
import com.eternal.base.data.ble.BleStatue;
import com.eternal.base.data.ble.IBleControl;
import com.eternal.base.data.source.ILogSource;
import com.eternal.base.database.entity.Log;
import com.eternal.base.database.entity.Notification;
import com.eternal.base.global.ActivityEvent;
import com.eternal.base.global.BluetoothKey;
import com.eternal.base.protocol.ProtocolTransformer;
import com.eternal.base.router.RouterActivityPath;
import com.eternal.framework.component.AppManager;
import com.eternal.framework.utils.GsonUtils;
import com.eternal.framework.utils.KLog;
import com.eternal.framework.utils.Utils;

import java.util.List;

import io.reactivex.Completable;
import io.reactivex.CompletableEmitter;
import io.reactivex.CompletableOnSubscribe;
import io.reactivex.Maybe;
import io.reactivex.MaybeEmitter;
import io.reactivex.MaybeOnSubscribe;
import io.reactivex.MaybeSource;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import me.leolin.shortcutbadger.ShortcutBadger;

public class LogRepository extends BaseRepository {
    private ILogSource source;
    private int notifySize;
    private NotificationDialog dialog;
    private Intent cache;
    private String cacheStr;

    public LogRepository(ILogSource source, IBleControl control) {
        super(control);
        this.source = source;
    }

    public DataSource.Factory<Integer, Log> getLogFactory(String mac, byte port, List<Byte> types, List<Byte> models) {
        return source.getLogs(mac, port, types, models);
    }

    public DataSource.Factory<Integer, Log> getLogFactory(String mac, List<Byte> types) {
        return source.getLogs(mac, types);
    }

    public Completable resetTime(final String mac, final byte port) {
        return Completable.create(new CompletableOnSubscribe() {
            @Override
            public void subscribe(CompletableEmitter emitter) throws Exception {
                source.setLogTime(mac, port, source.getFirstLog(mac).time);
                emitter.onComplete();
            }
        });
    }

    public Completable resetCTime(final String mac) {
        return Completable.create(new CompletableOnSubscribe() {
            @Override
            public void subscribe(CompletableEmitter emitter) throws Exception {
                source.setLogTime(mac, (byte) 0x0, source.getFirstCLog(mac).time);
                emitter.onComplete();
            }
        });
    }


    public void clearNotify() {
        notifySize = 0;
    }

    public Single<LogExtra> getExtra(final String mac) {
        return getExtra(mac, (byte) 0x0);
    }

    public Single<LogExtra> getExtra(final String mac, final byte port) {
        return Single.create(new SingleOnSubscribe<LogExtra>() {
            @Override
            public void subscribe(SingleEmitter<LogExtra> emitter) throws Exception {
                List<NotificationName> names = source.getNotificationNames(mac, port);
                SparseArray<String> list = new SparseArray<>(names.size());
                for (NotificationName name : names) {
                    list.put(name.id + 50 * name.type, name.name);
                }
                emitter.onSuccess(new LogExtra(source.getLogTime(mac, port), source.isDegree(mac), list));
            }
        });
    }

    public Completable initLog(final BleStatue statue, final byte port) {
        if (statue.getType() == BluetoothKey.DEVICE_TYPE_A || statue.getType() == BluetoothKey.DEVICE_TYPE_B) {
            return refreshLog(statue).ignoreElement().doOnComplete(new Action() {
                @Override
                public void run() throws Exception {
                    KLog.e("on complete set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            }).doOnError(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    KLog.e("on error set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            });

        } else if (statue.getType() == BluetoothKey.DEVICE_TYPE_E) { // 需要初始化所有端口？这里以后梳理
            return refreshELog(statue, port).ignoreElement().doOnComplete(new Action() {
                @Override
                public void run() throws Exception {
                    KLog.e("on complete set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            }).doOnError(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    KLog.e("on error set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            });
        } else {
            return refreshCLog(statue).ignoreElement().doOnComplete(new Action() {
                @Override
                public void run() throws Exception {
                    KLog.e("on complete set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstCLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            }).doOnError(new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    KLog.e("on error set time");
                    String mac = statue.getDevice().getMac();
                    Log log = source.getFirstCLog(mac);
                    source.setLogTime(mac, port, log.time);
                }
            });

        }
    }


    public Single<Integer> getNewLogNum(final String mac, final byte port) {
        return Single.create(new SingleOnSubscribe<Integer>() {
            @Override
            public void subscribe(SingleEmitter<Integer> emitter) throws Exception {
                if (source.getLogTime(mac, port) == 0) {
                    emitter.onSuccess(0);
                } else {
                    emitter.onSuccess(source.getNewLogNum(mac, port));
                }
            }
        });
    }

    public Single<Long> getDeviceTime(final String mac) {
        return Single.create(new SingleOnSubscribe<Long>() {
            @Override
            public void subscribe(SingleEmitter<Long> emitter) throws Exception {
                emitter.onSuccess(source.getDeviceConnectTime(mac));
            }
        }).subscribeOn(Schedulers.io());
    }

    public Maybe<Integer> refreshCLog(final BleStatue statue) {
        return Single.create(new SingleOnSubscribe<Log>() {
            @Override
            public void subscribe(@NonNull SingleEmitter<Log> emitter) {
                emitter.onSuccess(source.getFirstCLog(statue.getMac()));
            }
        }).flatMapMaybe(new Function<Log, MaybeSource<? extends Integer>>() {
            private boolean hasNew;

            @Override
            public MaybeSource<? extends Integer> apply(@NonNull Log log) {
                hasNew = false;
                return control.getCLog(statue, (log.time == 0 ? 0 : log.time / 1000 - 1577808000) / 60).observeOn(Schedulers.io()).doOnNext(new Consumer<Log>() {
                    @Override
                    public void accept(Log log) {
                        if (!(log.time == 0 || log.id == -1 || log.model == 0)) {
                            log.time = (log.time * 60 + 1577808000) * 1000;
                            log.mac = statue.getMac();
                            try {
                                source.addLog(log);
                                hasNew = true;
                            } catch (Exception e) {
                                KLog.e(e);
                            }
                        }
                    }
                }).ignoreElements().andThen(Maybe.create(new MaybeOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(@NonNull MaybeEmitter<Integer> emitter) {
                        if (hasNew) {
                            emitter.onSuccess(source.getNewLogNum(statue.getMac(), (byte) 0x0));
                        } else {
                            emitter.onComplete();
                        }
                    }
                }));
            }
        });
    }

    public Maybe<Integer> refreshLog(final BleStatue statue) {
        return Single.create(new SingleOnSubscribe<Log>() {
            @Override
            public void subscribe(SingleEmitter<Log> emitter) {
                emitter.onSuccess(source.getFirstLog(statue.getDevice().getMac()));
            }
        }).flatMapMaybe(new Function<Log, Maybe<Integer>>() {
            boolean hasNew;
            Log last;

            @Override
            public Maybe<Integer> apply(Log log) {
                hasNew = false;
                return control.getLog(statue, log.time, log.id).observeOn(Schedulers.io()).doOnNext(new Consumer<Log>() {
                    @Override
                    public void accept(Log log) {
                        KLog.e("收到logAB........" + GsonUtils.toJson(log));
                        if (checkLog(log)) {
                            log.mac = statue.getDevice().getMac();
                            try {
                                source.addLog(log);
                                hasNew = true;
                                if (log.logType == Log.TYPE_NOTIFY && System.currentTimeMillis() - 60000 <= log.time) {
                                    last = log;
                                }
                            } catch (Exception e) {
                                KLog.e(e);
                            }
                        }
                    }
                }).ignoreElements().andThen(Maybe.create(new MaybeOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(MaybeEmitter<Integer> emitter) throws Exception {
                        if (last != null) {
                            showNotify(last);
                            last = null;
                        }
                        if (hasNew) {
                            emitter.onSuccess(source.getNewLogNum(statue.getDevice().getMac(), (byte) 0x0));
                        } else {
                            emitter.onComplete();
                        }
                    }
                }));
            }
        });
    }

    public Maybe<Integer> refreshELog(final BleStatue statue, final byte port) {
        KLog.e("查询日志");
        return Single.create(new SingleOnSubscribe<Log>() {
            @Override
            public void subscribe(SingleEmitter<Log> emitter) {
                emitter.onSuccess(source.getFirstELog(statue.getDevice().getMac(), port));
            }
        }).flatMapMaybe(new Function<Log, Maybe<Integer>>() {
            boolean hasNew;
            Log last;

            @Override
            public Maybe<Integer> apply(Log log) {
                hasNew = false;
                return control.getELog(statue, port, log.time, log.id).observeOn(Schedulers.io()).doOnNext(new Consumer<Log>() {
                    @Override
                    public void accept(Log log) {
                        log.port = port;
                        KLog.e("收到logAB........" + GsonUtils.toJson(log));
                        if (checkLog(log)) {
                            log.mac = statue.getDevice().getMac();
                            try {
                                source.addLog(log);
                                hasNew = true;
                                if (log.logType == Log.TYPE_NOTIFY && System.currentTimeMillis() - 60000 <= log.time) {
                                    last = log;
                                }
                                KLog.e("添加log........");
                            } catch (Exception e) {
                                KLog.e(e);
                            }
                        }
                    }
                }).ignoreElements().andThen(Maybe.create(new MaybeOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(MaybeEmitter<Integer> emitter) throws Exception {
                        if (last != null) {
                            showNotify(last);
                            last = null;
                        }
                        if (hasNew) {
                            emitter.onSuccess(source.getNewLogNum(statue.getDevice().getMac(), port));
                        } else {
                            emitter.onComplete();
                        }
                    }
                }));
            }
        });
    }

    private boolean checkLog(Log log) {
        if (log.logType == 0x0 && log.notifyId != 0x0) {
            return false;
        }
        if (log.logType != 0x1 && (log.start != 0 || log.end != 0)) {
            return false;
        }
        if (log.model < 1 || log.model > 8) {
            return false;
        }
        return true;
    }

    @SuppressLint("CheckResult")
    private void showNotify(final Log log) {
        Single.create(new SingleOnSubscribe<String>() {
            @Override
            public void subscribe(SingleEmitter<String> emitter) throws Exception {
                Notification notification = source.getNotification(log.mac, log.port, log.notifyId, log.logType);
                emitter.onSuccess(ProtocolTransformer.getNotifyString(log, notification.name,
                        source.getModelInfo(log.mac), source.isDegree(log.mac)));
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) {
                Postcard postcard = ARouter.getInstance().build(RouterActivityPath.Main.PAGE_MAIN);
                LogisticsCenter.completion(postcard);
                Intent intent = new Intent(Utils.getContext(), postcard.getDestination());
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setAction(ActivityEvent.ACTION_LOG);
                intent.putExtra(ActivityEvent.DEVICE_MAC, log.mac);
                if (AppManager.getAppManager().isBack()) {
                    ShortcutBadger.applyCount(Utils.getContext(), ++notifySize);
                    PendingIntent pendingIntent = PendingIntent.getActivity(Utils.getContext(), 0,
                            intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    NotificationManager manager = (NotificationManager) Utils.getContext()
                            .getSystemService(Context.NOTIFICATION_SERVICE);
                    NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle()
                            .bigText(s);
                    android.app.Notification notification = new NotificationCompat.Builder(Utils.getContext(), "notification")
                            .setStyle(style)
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .setLargeIcon(BitmapFactory.decodeResource(Utils.getApp().getResources(), R.mipmap.ic_launcher))
                            .setAutoCancel(true).setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
                            .setContentText(s).setContentIntent(pendingIntent)
                            .setFullScreenIntent(pendingIntent, true)
                            .setPriority(NotificationCompat.PRIORITY_MAX).build();
                    manager.notify(1, notification);
                } else {
                    if (dialog != null && dialog.isShowing()) {
                        cache = intent;
                        cacheStr = s;
                    } else {
                        NotificationDialog temp = new NotificationDialog(AppManager.getAppManager().currentActivity());
                        temp.setSense(2000).setMessage(s).setIntent(intent);
                        temp.setDismissListener(notificationListener).show();
                    }
                }
            }
        });
    }

    private DialogInterface.OnDismissListener notificationListener = new DialogInterface.OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialog) {
            if (cache != null) {
                new NotificationDialog(AppManager.getAppManager().currentActivity()).setSense(2000)
                        .setMessage(cacheStr).setIntent(cache).setDismissListener(this).show();
                cache = null;
                cacheStr = null;
            } else {
                LogRepository.this.dialog = null;
            }
        }
    };
}
